#install.packages("knitr")
#install.packages("grid")
#install.packages("MLRMPA")
#install.packages("dprep")
#install.packages("normalr")
#install.packages("ggcorrplot")
library(gridExtra)
library(dplyr)
library(lubridate)
library(magrittr)
library(ggplot2)
library(tidyr)
library(knitr)
library(normalr)
library(ggcorrplot)
#library(MLRMPA)
#??src_mysql
my_db <- src_mysql(
dbname = "covid",
host = "localhost",
user = "root",
password = "1234"
)
my_db
src: mysql 8.0.21 [root@localhost:/covid]
tbls: citytemperature, covid_confirmed_yearly, covid_deaths_yearly, covid_recovered_yearly, covid19_confirmed_global, covid19_deaths_global,
covid19_recovered_global, full_datas, gdp, gdp19, gdp2019, healthranking, imf-country, owid-covid-data, population, temp2019, world_temp
##import data
df_conf <- tbl(my_db, sql("select * from covid_confirmed_yearly"))
df_conf <- as.data.frame(df_conf)
df_conf
df_deaths <- tbl(my_db, sql("select * from covid_deaths_yearly"))
df_deaths <- as.data.frame(df_deaths)
df_deaths
df_recover <- tbl(my_db, sql("select * from covid_recovered_yearly"))
df_recover <- as.data.frame(df_recover)
df_recover
##check the time frame of the data
n.col <- ncol(df_conf)
dates <- names(df_conf)[5:n.col]%>% mdy()
range(dates)
[1] "2020-01-22" "2021-01-11"
min.date <- min(dates)
max.date <- max(dates)
min.date.txt <- min.date %>% format('%d %b %Y')
max.date.txt <- max.date %>% format('%d %b %Y')
#clean data
cleanData <- function(data) {
## remove some columns
data %<>% select(-c(Province.State, Lat, Long)) %>% rename(country=Country.Region)
## convert from wide to long format
data %<>% gather(key=date, value=count, -country)
## convert from character to date
data %<>% mutate(date = date %>% mdy())
## aggregate by country
data %<>% group_by(country, date) %>% summarise(count=sum(count, na.rm=T)) %>% as.data.frame()
return(data)
}
## clean the three data sets
data.confirmed <- df_conf %>% cleanData() %>% rename(confirmed=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.deaths <- df_deaths %>% cleanData() %>% rename(deaths=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data.recovered <- df_recover %>% cleanData() %>% rename(recovered=count)
`summarise()` regrouping output by 'country' (override with `.groups` argument)
data <- data.confirmed %>% merge(data.deaths, all=T) %>% merge(data.recovered, all=T)
data
## countries/regions with confirmed cases, excl. cruise ships
countries <- data %>% pull(country) %>% setdiff('Cruise Ship')
data
data.world <- data %>% group_by(date) %>%
summarise(country='World',
confirmed = sum(confirmed, na.rm=T),
deaths = sum(deaths, na.rm=T),
recovered = sum(recovered, na.rm=T))
`summarise()` ungrouping output (override with `.groups` argument)
data %<>% rbind(data.world)
data
data %<>% mutate(current.confirmed = confirmed - deaths - recovered)
View(data)
#rate
data %<>% arrange(country, date)
n <- nrow(data)
day1 <- min(data$date)
data %<>% mutate(new.confirmed = ifelse(date == day1, NA, confirmed - lag(confirmed, n=1)),
new.deaths = ifelse(date == day1, NA, deaths - lag(deaths, n=1)),
new.recovered = ifelse(date == day1, NA, recovered - lag(recovered, n=1)))
data %<>% mutate(new.confirmed = ifelse(new.confirmed < 0, 0, new.confirmed),
new.deaths = ifelse(new.deaths < 0, 0, new.deaths),
new.recovered = ifelse(new.recovered < 0, 0, new.recovered))
## death rate based on total deaths and recovered cases
data %<>% mutate(rate.upper = (100 * deaths / (deaths + recovered)) %>% round(1))
## lower bound: death rate based on total confirmed cases
data %<>% mutate(rate.lower = (100 * deaths / confirmed) %>% round(1))
## death rate based on the number of death/recovered on every single day
data %<>% mutate(rate.daily = (100 * new.deaths / (new.deaths + new.recovered)) %>% round(1))
View(data)
## convert from wide to long format
data.long <- data %>%
select(c(country, date, confirmed, current.confirmed, recovered, deaths)) %>%
gather(key=type, value=count, -c(country, date))
## set factor levels to show them in a desirable order
data.long %<>% mutate(type=recode_factor(type, confirmed='Total Confirmed',
current.confirmed='Current Confirmed',
recovered='Recovered',
deaths='Deaths'))
View(data.long)
##Number of case World
world <- filter(data.long,country == 'World')
plot1 <- world %>% filter(type != 'Total Confirmed') %>%
ggplot(aes(x=date, y=count)) +
geom_area(aes(fill=type), alpha=0.5) +
labs(title=paste0('Numbers of Cases Worldwide - ', max.date.txt)) +
scale_fill_manual(values=c('red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1))
plot2 <- world %>%
ggplot(aes(x=date, y=count)) +
geom_line(aes(color=type)) +
labs(title=paste0('Numbers of Cases Worldwide (log scale) - ', max.date.txt)) +
scale_color_manual(values=c('purple', 'red', 'green', 'black')) +
theme(legend.title=element_blank(), legend.position='bottom',
plot.title = element_text(size=7),
axis.title.x=element_blank(),
axis.title.y=element_blank(),
legend.key.size=unit(0.2, 'cm'),
legend.text=element_text(size=6),
axis.text=element_text(size=7),
axis.text.x=element_text(angle=45, hjust=1)) +
scale_y_continuous(trans='log10')
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

## Current Confirmed Cases
data.world <- data %>% filter(country=='World')
n <- nrow(data.world)
plot1 <- ggplot(data.world, aes(x=date, y=current.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Current Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=new.confirmed)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Daily New Confirmed Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show two plots side by side
grid.arrange(plot1, plot2, ncol=2)

## a scatter plot with a smoothed line and vertical x-axis labels
plot1 <- ggplot(data.world, aes(x=date, y=deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot2 <- ggplot(data.world, aes(x=date, y=recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='Accumulative Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot3 <- ggplot(data.world, aes(x=date, y=new.deaths)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Deaths') +
theme(axis.text.x=element_text(angle=45, hjust=1))
plot4 <- ggplot(data.world, aes(x=date, y=new.recovered)) +
geom_point() + geom_smooth() +
xlab('') + ylab('Count') + labs(title='New Recovered Cases') +
theme(axis.text.x=element_text(angle=45, hjust=1))
## show four plots together, with 2 plots in each row
grid.arrange(plot1, plot2, plot3, plot4, nrow=2)

## convert from wide to long format, for drawing area plots
rates.long <- data %>%
select(c(country, date, rate.upper, rate.lower, rate.daily)) %>%
gather(key=type, value=count, -c(country, date))
# set factor levels to show them in a desirable order
rates.long %<>% mutate(type=recode_factor(type, rate.daily='Daily',
rate.upper='Upper bound'))
## ranking by confirmed cases
data.latest.all <- data %>% filter(date == max(date)) %>%
select(country, date,confirmed, new.confirmed, current.confirmed,
recovered, deaths, new.deaths, death.rate=rate.lower) %>%
mutate(ranking = dense_rank(desc(confirmed)))
#View(data.latest.all)
k <- 20
## top 20 countries: 21 incl. 'World'
top.countries <- data.latest.all %>% filter(ranking <= k + 1) %>%
arrange(ranking) %>% pull(country) %>% as.character()
top.countries %>% setdiff('World') %>% print()
[1] "US" "India" "Brazil" "Russia" "United Kingdom" "France" "Turkey" "Italy"
[9] "Spain" "Germany" "Colombia" "Argentina" "Mexico" "Poland" "Iran" "South Africa"
[17] "Ukraine" "Peru" "Netherlands" "Indonesia"
data.latest <- data.latest.all %>% filter(!is.na(country)) %>%
mutate(country=ifelse(ranking <= k + 1, as.character(country), 'Others')) %>%
mutate(country=country %>% factor(levels=c(top.countries, 'Others')))
data.latest %<>% group_by(country) %>%
summarise(confirmed=sum(confirmed), new.confirmed=sum(new.confirmed),
current.confirmed=sum(current.confirmed),
recovered=sum(recovered), deaths=sum(deaths), new.deaths=sum(new.deaths)) %>%
mutate(death.rate=(100 * deaths/confirmed) %>% round(1))
`summarise()` ungrouping output (override with `.groups` argument)
data.latest
data.latest %<>% select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths, current.confirmed,recovered)) %>%
mutate(recover.rate=(100 * recovered/confirmed) %>% round(1))
data.latest
df_pop <- tbl(my_db, sql("select * from population "))
df_pop <- as.data.frame(df_pop)
df_pop <- rename(df_pop,"country"="Country")
# Add World Population
world_pop <- sum(df_pop$`Population (2020)`)
df_pop[nrow(df_pop) + 1,] = c("World", world_pop)
# Add Other Countries Population
top_pop <- filter(df_pop, df_pop$country %in% top.countries & df_pop$country != "World")
top_pop <- sum(top_pop$`Population (2020)` %>% as.numeric())
others_pop <- (world_pop - top_pop)
df_pop[nrow(df_pop) + 1,] = c("Others", others_pop)
#View(df_pop)
data.latest <- merge(x = data.latest, y = df_pop, by = "country", all.x = TRUE)
data.latest
data.latest <- rename(data.latest,"population" = "Population (2020)")
data.latest$population <- data.latest$population %>% as.numeric()
data.latest <- data.latest %>%
select(c(country, confirmed, deaths, death.rate,
new.confirmed, new.deaths,
current.confirmed, recovered, recover.rate, population)) %>%
mutate(confirm.rate = (100 * confirmed / population) %>% round(1))
data.latest
data.latest %>% mutate(death.rate=death.rate %>% format(nsmall=1) %>% paste0('%'))
NA
NA
## convert from wide to long format, for drawing area plots
data.latest.long <- data.latest %>% filter(country!='World') %>%
gather(key=type, value=count, -country)
## set factor levels to show them with proper text and in a desirable order
data.latest.long %<>% mutate(type=recode_factor(type,
confirmed='Total Confirmed',
deaths='Total Deaths',
death.rate='Death Rate (%)',
new.confirmed='New Confirmed (compared with one day before)',
new.deaths='New Deaths (compared with one day before)',
current.confirmed='Current Confirmed',
recover.rate = 'Recover Rate(%)',
confirm.rate = 'Confirmed Rate(%)'))
#View(data.latest.long)
data.one.dem <- filter(data.latest.long,type=='Total Confirmed'
| type=='Total Deaths'
| type=='Current Confirmed')
data.two.dem <- filter(data.latest.long,type=='Death Rate (%)'
| type=='New Confirmed (compared with one day before)'
| type=='New Deaths (compared with one day before)'
| type=='Recover Rate(%)'
| type=='Confirmed Rate(%)')
data.two.dem
## bar chart
data.one.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

data.two.dem %>% ggplot(aes(x=country, y=count, fill=country, group=country)) +
geom_bar(stat='identity') +
geom_text(aes(label=count, y=count), size=3, vjust=0) +
xlab('') + ylab('') +
labs(title=paste0('Top 20 Countries with Most Confirmed Cases - ', max.date.txt)) +
scale_fill_discrete(name='Country', labels=aes(count)) +
theme(legend.title=element_blank(),
legend.position='none',
plot.title=element_text(size=13),
axis.text=element_text(size=8),
axis.text.x=element_text(angle=45, hjust=1)) +
facet_wrap(~type, ncol=1, scales='free_y')

##GDP
df_gdp <- tbl(my_db, sql("select * from gdp"))
df_gdp <- as.data.frame(df_gdp)
df_gdp <- rename(df_gdp,"country"="Real GDP growth (Annual percent change)")
df_gdp <- select(df_gdp,c("country","2012","2013","2014","2015","2016","2017","2018","2019","2020","2021"))
df_gdp
df_gdp2019 <- tbl(my_db, sql("select * from gdp19"))
df_gdp2019 <- as.data.frame(df_gdp2019)
df_gdp2019
NA
#healthranking
df_healt <- tbl(my_db, sql("select * from healthranking"))
df_healt <- as.data.frame(df_healt)
df_healt <- select(df_healt,c("country","healthCareIndex"))
View(df_healt)
#temp
df_temp <- tbl(my_db, sql("select * from temp"))
Error in .local(conn, statement, ...) :
could not run statement: Table 'covid.temp' doesn't exist
#Top 20 with gdp
data.longGDP <- df_gdp %>% gather(key=year, value=GDP, -c(country))
data.top <- data.latest %>% filter(country!='World'& country!='Others')
#data.top <- head(data.top,20)
View(data.top)
data.gdp <- filter(data.longGDP,year=='2020')
#View(data.gdp)
#merge
mergcountry = function(data1,data2){
data <- merge(x = data1, y = data2, by = "country", all.x = TRUE)
return(data)
}
data.top.world <- merge(x = data.top, y = df_gdp2019, by = "country", all.x = TRUE) %>%
select(-c(code,rank,new.confirmed,new.deaths,current.confirmed,population)) %>%
rename(GDP="GDP (millions of US dollars)")
data.top.world <- merge(x = data.top.world, y = df_healt, by = "country", all.x = TRUE) %>%
rename(healthcare="healthCareIndex")
#data.top.world <- mergcountry(data.top.world, df_temp)
View(data.top.world)
normalize = function(data){
#return ((data - min(data,na.rm = TRUE))/(max(data,na.rm = TRUE) - min(data,na.rm = TRUE)))
z <- scale(data);
tanh(z/2)
}
norm_data = as.data.frame(apply(data.top.world[,2:9],2,normalize))
corr_data <- norm_data
norm_data$country <- c("Argentina","Bangladesh","Brazil","Chile","Colombia","France","Germany","India","Iran","Italy","Mexico","Pakistan","Peru","Russia","saudi Arabia","South Africa","Spain","Turkey","United Kingdom","US")
View(norm_data)
norm_data_plot <- select(norm_data,"country","confirm.rate","death.rate","recover.rate","healthcare","GDP")
norm_data_plot %<>% gather(key=type, value=count, -c(country))
level_order <- factor(norm_data_plot$type,
level = c("GDP","healthcare","recover.rate","death.rate","confirm.rate"))
ggplot(data = norm_data_plot, aes(x=country, y=level_order, fill=count)) +
geom_tile() +
scale_fill_gradient(low = "pink", high = "blue") +
xlab("") +
ylab("") +
theme_bw() +
theme(axis.text.x = element_text(angle = 90,vjust = 1))+
theme(
axis.line = element_blank(),
axis.ticks = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
#legend.position = "none"
)

NA
#rank GDP
data.top.hight <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(desc(GDP)))
data.top.hight
k <- 15
top.gdp <- data.top.hight %>%
#filter(ranking <= k + 1) %>%
arrange(ranking)
top.gdp <- head(top.gdp,21)
data.top.low <- data.gdp %>% select(country, year,GDP) %>%
mutate(ranking = dense_rank(GDP))
low.gdp.long <- data.top.low %>%
#filter(ranking <= k + 1) %>%
arrange(ranking)
View(low.gdp.long)
low.gdp <- head(low.gdp.long,23)
low.gdp
#correlation
corr_data %<>% select(c(GDP,confirm.rate,death.rate,recover.rate,healthcare))
head(corr_data)
cor(corr_data)
GDP confirm.rate death.rate recover.rate healthcare
GDP 1.0000000 0.4297936 -0.2360041 -0.3765615 0.2518394
confirm.rate 0.4297936 1.0000000 -0.3345825 -0.6868148 0.4543381
death.rate -0.2360041 -0.3345825 1.0000000 0.1626707 -0.1470020
recover.rate -0.3765615 -0.6868148 0.1626707 1.0000000 -0.7549792
healthcare 0.2518394 0.4543381 -0.1470020 -0.7549792 1.0000000
ggcorrplot(cor(corr_data),hc.order = TRUE,
outline.color = "white",
colors = c("#6D9EC1","white","#E46726"),
lab = TRUE)

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdyaWQiKQ0KI2luc3RhbGwucGFja2FnZXMoIk1MUk1QQSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHByZXAiKQ0KI2luc3RhbGwucGFja2FnZXMoIm5vcm1hbHIiKQ0KI2luc3RhbGwucGFja2FnZXMoImdnY29ycnBsb3QiKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KG5vcm1hbHIpDQpsaWJyYXJ5KGdnY29ycnBsb3QpDQojbGlicmFyeShNTFJNUEEpDQojPz9zcmNfbXlzcWwNCm15X2RiIDwtIHNyY19teXNxbCgNCiAgZGJuYW1lID0gImNvdmlkIiwNCiAgaG9zdCA9ICJsb2NhbGhvc3QiLA0KICB1c2VyID0gInJvb3QiLA0KICBwYXNzd29yZCA9ICIxMjM0Ig0KKQ0KbXlfZGINCg0KIyNpbXBvcnQgZGF0YQ0KZGZfY29uZiA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZF9jb25maXJtZWRfeWVhcmx5IikpDQpkZl9jb25mIDwtIGFzLmRhdGEuZnJhbWUoZGZfY29uZikNCmRmX2NvbmYNCmRmX2RlYXRocyA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBjb3ZpZF9kZWF0aHNfeWVhcmx5IikpDQpkZl9kZWF0aHMgPC0gYXMuZGF0YS5mcmFtZShkZl9kZWF0aHMpDQpkZl9kZWF0aHMNCmRmX3JlY292ZXIgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gY292aWRfcmVjb3ZlcmVkX3llYXJseSIpKQ0KZGZfcmVjb3ZlciA8LSBhcy5kYXRhLmZyYW1lKGRmX3JlY292ZXIpDQpkZl9yZWNvdmVyDQpgYGANCmBgYHtyfQ0KIyNjaGVjayB0aGUgdGltZSBmcmFtZSBvZiB0aGUgZGF0YQ0Kbi5jb2wgPC0gbmNvbChkZl9jb25mKQ0KZGF0ZXMgPC0gbmFtZXMoZGZfY29uZilbNTpuLmNvbF0lPiUgbWR5KCkNCnJhbmdlKGRhdGVzKQ0KbWluLmRhdGUgPC0gbWluKGRhdGVzKQ0KbWF4LmRhdGUgPC0gbWF4KGRhdGVzKQ0KbWluLmRhdGUudHh0IDwtIG1pbi5kYXRlICU+JSBmb3JtYXQoJyVkICViICVZJykNCm1heC5kYXRlLnR4dCA8LSBtYXguZGF0ZSAlPiUgZm9ybWF0KCclZCAlYiAlWScpDQpgYGANCmBgYHtyfQ0KI2NsZWFuIGRhdGENCmNsZWFuRGF0YSA8LSBmdW5jdGlvbihkYXRhKSB7DQogICMjIHJlbW92ZSBzb21lIGNvbHVtbnMNCiAgZGF0YSAlPD4lIHNlbGVjdCgtYyhQcm92aW5jZS5TdGF0ZSwgTGF0LCBMb25nKSkgJT4lIHJlbmFtZShjb3VudHJ5PUNvdW50cnkuUmVnaW9uKQ0KICAjIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KICBkYXRhICU8PiUgZ2F0aGVyKGtleT1kYXRlLCB2YWx1ZT1jb3VudCwgLWNvdW50cnkpDQogICMjIGNvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gZGF0ZQ0KICBkYXRhICU8PiUgbXV0YXRlKGRhdGUgPSBkYXRlICU+JSBtZHkoKSkNCiAgIyMgYWdncmVnYXRlIGJ5IGNvdW50cnkNCiAgZGF0YSAlPD4lIGdyb3VwX2J5KGNvdW50cnksIGRhdGUpICU+JSBzdW1tYXJpc2UoY291bnQ9c3VtKGNvdW50LCBuYS5ybT1UKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQ0KICByZXR1cm4oZGF0YSkNCn0NCiMjIGNsZWFuIHRoZSB0aHJlZSBkYXRhIHNldHMNCmRhdGEuY29uZmlybWVkIDwtIGRmX2NvbmYgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUoY29uZmlybWVkPWNvdW50KQ0KZGF0YS5kZWF0aHMgPC0gZGZfZGVhdGhzICU+JSBjbGVhbkRhdGEoKSAlPiUgcmVuYW1lKGRlYXRocz1jb3VudCkNCmRhdGEucmVjb3ZlcmVkIDwtIGRmX3JlY292ZXIgJT4lIGNsZWFuRGF0YSgpICU+JSByZW5hbWUocmVjb3ZlcmVkPWNvdW50KQ0KZGF0YSA8LSBkYXRhLmNvbmZpcm1lZCAlPiUgbWVyZ2UoZGF0YS5kZWF0aHMsIGFsbD1UKSAlPiUgbWVyZ2UoZGF0YS5yZWNvdmVyZWQsIGFsbD1UKQ0KZGF0YQ0KIyMgY291bnRyaWVzL3JlZ2lvbnMgd2l0aCBjb25maXJtZWQgY2FzZXMsIGV4Y2wuIGNydWlzZSBzaGlwcw0KY291bnRyaWVzIDwtIGRhdGEgJT4lIHB1bGwoY291bnRyeSkgJT4lIHNldGRpZmYoJ0NydWlzZSBTaGlwJykNCmRhdGENCg0KZGF0YS53b3JsZCA8LSBkYXRhICU+JSBncm91cF9ieShkYXRlKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50cnk9J1dvcmxkJywNCiAgICAgICAgICAgIGNvbmZpcm1lZCA9IHN1bShjb25maXJtZWQsIG5hLnJtPVQpLA0KICAgICAgICAgICAgZGVhdGhzID0gc3VtKGRlYXRocywgbmEucm09VCksDQogICAgICAgICAgICByZWNvdmVyZWQgPSBzdW0ocmVjb3ZlcmVkLCBuYS5ybT1UKSkNCmRhdGEgJTw+JSByYmluZChkYXRhLndvcmxkKQ0KZGF0YQ0KZGF0YSAlPD4lIG11dGF0ZShjdXJyZW50LmNvbmZpcm1lZCA9IGNvbmZpcm1lZCAtIGRlYXRocyAtIHJlY292ZXJlZCkNClZpZXcoZGF0YSkNCg0KYGBgDQpgYGB7cn0NCiNyYXRlDQpkYXRhICU8PiUgYXJyYW5nZShjb3VudHJ5LCBkYXRlKQ0KbiA8LSBucm93KGRhdGEpDQpkYXkxIDwtIG1pbihkYXRhJGRhdGUpDQpkYXRhICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgY29uZmlybWVkIC0gbGFnKGNvbmZpcm1lZCwgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5kZWF0aHMgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgZGVhdGhzIC0gbGFnKGRlYXRocywgbj0xKSksDQogICAgICAgICAgICAgICAgIG5ldy5yZWNvdmVyZWQgPSBpZmVsc2UoZGF0ZSA9PSBkYXkxLCBOQSwgcmVjb3ZlcmVkIC0gbGFnKHJlY292ZXJlZCwgbj0xKSkpDQpkYXRhICU8PiUgbXV0YXRlKG5ldy5jb25maXJtZWQgPSBpZmVsc2UobmV3LmNvbmZpcm1lZCA8IDAsIDAsIG5ldy5jb25maXJtZWQpLA0KICAgICAgICAgICAgICAgICBuZXcuZGVhdGhzID0gaWZlbHNlKG5ldy5kZWF0aHMgPCAwLCAwLCBuZXcuZGVhdGhzKSwNCiAgICAgICAgICAgICAgICAgbmV3LnJlY292ZXJlZCA9IGlmZWxzZShuZXcucmVjb3ZlcmVkIDwgMCwgMCwgbmV3LnJlY292ZXJlZCkpDQojIyBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGRlYXRocyBhbmQgcmVjb3ZlcmVkIGNhc2VzDQpkYXRhICU8PiUgbXV0YXRlKHJhdGUudXBwZXIgPSAoMTAwICogZGVhdGhzIC8gKGRlYXRocyArIHJlY292ZXJlZCkpICU+JSByb3VuZCgxKSkNCiMjIGxvd2VyIGJvdW5kOiBkZWF0aCByYXRlIGJhc2VkIG9uIHRvdGFsIGNvbmZpcm1lZCBjYXNlcw0KZGF0YSAlPD4lIG11dGF0ZShyYXRlLmxvd2VyID0gKDEwMCAqIGRlYXRocyAvIGNvbmZpcm1lZCkgJT4lIHJvdW5kKDEpKQ0KIyMgZGVhdGggcmF0ZSBiYXNlZCBvbiB0aGUgbnVtYmVyIG9mIGRlYXRoL3JlY292ZXJlZCBvbiBldmVyeSBzaW5nbGUgZGF5DQpkYXRhICU8PiUgbXV0YXRlKHJhdGUuZGFpbHkgPSAoMTAwICogbmV3LmRlYXRocyAvIChuZXcuZGVhdGhzICsgbmV3LnJlY292ZXJlZCkpICU+JSByb3VuZCgxKSkNClZpZXcoZGF0YSkNCmBgYA0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdA0KZGF0YS5sb25nIDwtIGRhdGEgJT4lDQogIHNlbGVjdChjKGNvdW50cnksIGRhdGUsIGNvbmZpcm1lZCwgY3VycmVudC5jb25maXJtZWQsIHJlY292ZXJlZCwgZGVhdGhzKSkgJT4lDQogIGdhdGhlcihrZXk9dHlwZSwgdmFsdWU9Y291bnQsIC1jKGNvdW50cnksIGRhdGUpKQ0KIyMgc2V0IGZhY3RvciBsZXZlbHMgdG8gc2hvdyB0aGVtIGluIGEgZGVzaXJhYmxlIG9yZGVyDQpkYXRhLmxvbmcgJTw+JSBtdXRhdGUodHlwZT1yZWNvZGVfZmFjdG9yKHR5cGUsIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudC5jb25maXJtZWQ9J0N1cnJlbnQgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3ZlcmVkPSdSZWNvdmVyZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWF0aHM9J0RlYXRocycpKQ0KVmlldyhkYXRhLmxvbmcpDQpgYGANCmBgYHtyfQ0KIyNOdW1iZXIgb2YgY2FzZSBXb3JsZA0Kd29ybGQgPC0gZmlsdGVyKGRhdGEubG9uZyxjb3VudHJ5ID09ICdXb3JsZCcpDQpwbG90MSA8LSB3b3JsZCAlPiUgZmlsdGVyKHR5cGUgIT0gJ1RvdGFsIENvbmZpcm1lZCcpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1jb3VudCkpICsNCiAgZ2VvbV9hcmVhKGFlcyhmaWxsPXR5cGUpLCBhbHBoYT0wLjUpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygncmVkJywgJ2dyZWVuJywgJ2JsYWNrJykpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLA0KICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5rZXkuc2l6ZT11bml0KDAuMiwgJ2NtJyksDQogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYpLA0KICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NyksDQogICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MiA8LSB3b3JsZCAlPiUNCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9Y291bnQpKSArDQogIGdlb21fbGluZShhZXMoY29sb3I9dHlwZSkpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ051bWJlcnMgb2YgQ2FzZXMgV29ybGR3aWRlIChsb2cgc2NhbGUpIC0gJywgbWF4LmRhdGUudHh0KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJ3B1cnBsZScsICdyZWQnLCAnZ3JlZW4nLCAnYmxhY2snKSkgKw0KICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplPXVuaXQoMC4yLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NiksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT03KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9J2xvZzEwJykNCiMjIHNob3cgdHdvIHBsb3RzIHNpZGUgYnkgc2lkZQ0KZ3JpZC5hcnJhbmdlKHBsb3QxLCBwbG90MiwgbmNvbD0yKQ0KYGBgDQpgYGB7cn0NCiMjIEN1cnJlbnQgQ29uZmlybWVkIENhc2VzDQpkYXRhLndvcmxkIDwtIGRhdGEgJT4lIGZpbHRlcihjb3VudHJ5PT0nV29ybGQnKQ0KbiA8LSBucm93KGRhdGEud29ybGQpDQpwbG90MSA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1jdXJyZW50LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0N1cnJlbnQgQ29uZmlybWVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KcGxvdDIgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9bmV3LmNvbmZpcm1lZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0RhaWx5IE5ldyBDb25maXJtZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQojIyBzaG93IHR3byBwbG90cyBzaWRlIGJ5IHNpZGUNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIG5jb2w9MikNCmBgYA0KYGBge3J9DQojIyBhIHNjYXR0ZXIgcGxvdCB3aXRoIGEgc21vb3RoZWQgbGluZSBhbmQgdmVydGljYWwgeC1heGlzIGxhYmVscw0KcGxvdDEgPC0gZ2dwbG90KGRhdGEud29ybGQsIGFlcyh4PWRhdGUsIHk9ZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nQWNjdW11bGF0aXZlIERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3QyIDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PXJlY292ZXJlZCkpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSArDQogIHhsYWIoJycpICsgeWxhYignQ291bnQnKSArIGxhYnModGl0bGU9J0FjY3VtdWxhdGl2ZSBSZWNvdmVyZWQgQ2FzZXMnKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpDQpwbG90MyA8LSBnZ3Bsb3QoZGF0YS53b3JsZCwgYWVzKHg9ZGF0ZSwgeT1uZXcuZGVhdGhzKSkgKw0KICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpICsNCiAgeGxhYignJykgKyB5bGFiKCdDb3VudCcpICsgbGFicyh0aXRsZT0nTmV3IERlYXRocycpICsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkNCnBsb3Q0IDwtIGdncGxvdChkYXRhLndvcmxkLCBhZXMoeD1kYXRlLCB5PW5ldy5yZWNvdmVyZWQpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJ0NvdW50JykgKyBsYWJzKHRpdGxlPSdOZXcgUmVjb3ZlcmVkIENhc2VzJykgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQ0KIyMgc2hvdyBmb3VyIHBsb3RzIHRvZ2V0aGVyLCB3aXRoIDIgcGxvdHMgaW4gZWFjaCByb3cNCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIsIHBsb3QzLCBwbG90NCwgbnJvdz0yKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMjIGNvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZm9ybWF0LCBmb3IgZHJhd2luZyBhcmVhIHBsb3RzDQpyYXRlcy5sb25nIDwtIGRhdGEgJT4lDQogIHNlbGVjdChjKGNvdW50cnksIGRhdGUsIHJhdGUudXBwZXIsIHJhdGUubG93ZXIsIHJhdGUuZGFpbHkpKSAlPiUNCiAgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSwgZGF0ZSkpDQojIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KcmF0ZXMubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwgcmF0ZS5kYWlseT0nRGFpbHknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYXRlLnVwcGVyPSdVcHBlciBib3VuZCcpKQ0KYGBgDQpgYGB7cn0NCiMjIHJhbmtpbmcgYnkgY29uZmlybWVkIGNhc2VzDQpkYXRhLmxhdGVzdC5hbGwgPC0gZGF0YSAlPiUgZmlsdGVyKGRhdGUgPT0gbWF4KGRhdGUpKSAlPiUNCiAgc2VsZWN0KGNvdW50cnksIGRhdGUsY29uZmlybWVkLCBuZXcuY29uZmlybWVkLCBjdXJyZW50LmNvbmZpcm1lZCwNCiAgICAgICAgIHJlY292ZXJlZCwgZGVhdGhzLCBuZXcuZGVhdGhzLCBkZWF0aC5yYXRlPXJhdGUubG93ZXIpICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhjb25maXJtZWQpKSkNCiNWaWV3KGRhdGEubGF0ZXN0LmFsbCkNCmsgPC0gMjANCiMjIHRvcCAyMCBjb3VudHJpZXM6IDIxIGluY2wuICdXb3JsZCcNCnRvcC5jb3VudHJpZXMgPC0gZGF0YS5sYXRlc3QuYWxsICU+JSBmaWx0ZXIocmFua2luZyA8PSBrICsgMSkgJT4lDQogIGFycmFuZ2UocmFua2luZykgJT4lIHB1bGwoY291bnRyeSkgJT4lIGFzLmNoYXJhY3RlcigpDQp0b3AuY291bnRyaWVzICU+JSBzZXRkaWZmKCdXb3JsZCcpICU+JSBwcmludCgpDQoNCmBgYA0KDQpgYGB7cn0NCmRhdGEubGF0ZXN0IDwtIGRhdGEubGF0ZXN0LmFsbCAlPiUgZmlsdGVyKCFpcy5uYShjb3VudHJ5KSkgJT4lDQogIG11dGF0ZShjb3VudHJ5PWlmZWxzZShyYW5raW5nIDw9IGsgKyAxLCBhcy5jaGFyYWN0ZXIoY291bnRyeSksICdPdGhlcnMnKSkgJT4lDQogIG11dGF0ZShjb3VudHJ5PWNvdW50cnkgJT4lIGZhY3RvcihsZXZlbHM9Yyh0b3AuY291bnRyaWVzLCAnT3RoZXJzJykpKQ0KZGF0YS5sYXRlc3QgJTw+JSBncm91cF9ieShjb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXNlKGNvbmZpcm1lZD1zdW0oY29uZmlybWVkKSwgbmV3LmNvbmZpcm1lZD1zdW0obmV3LmNvbmZpcm1lZCksDQogICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZD1zdW0oY3VycmVudC5jb25maXJtZWQpLA0KICAgICAgICAgICAgcmVjb3ZlcmVkPXN1bShyZWNvdmVyZWQpLCBkZWF0aHM9c3VtKGRlYXRocyksIG5ldy5kZWF0aHM9c3VtKG5ldy5kZWF0aHMpKSAlPiUNCiAgbXV0YXRlKGRlYXRoLnJhdGU9KDEwMCAqIGRlYXRocy9jb25maXJtZWQpICU+JSByb3VuZCgxKSkgDQpkYXRhLmxhdGVzdA0KZGF0YS5sYXRlc3QgJTw+JSBzZWxlY3QoYyhjb3VudHJ5LCBjb25maXJtZWQsIGRlYXRocywgZGVhdGgucmF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3LmNvbmZpcm1lZCwgbmV3LmRlYXRocywgY3VycmVudC5jb25maXJtZWQscmVjb3ZlcmVkKSkgJT4lDQogIG11dGF0ZShyZWNvdmVyLnJhdGU9KDEwMCAqIHJlY292ZXJlZC9jb25maXJtZWQpICU+JSByb3VuZCgxKSkNCmRhdGEubGF0ZXN0DQoNCmRmX3BvcCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBwb3B1bGF0aW9uICIpKQ0KZGZfcG9wIDwtIGFzLmRhdGEuZnJhbWUoZGZfcG9wKQ0KZGZfcG9wIDwtIHJlbmFtZShkZl9wb3AsImNvdW50cnkiPSJDb3VudHJ5IikNCg0KIyBBZGQgV29ybGQgUG9wdWxhdGlvbg0Kd29ybGRfcG9wIDwtIHN1bShkZl9wb3AkYFBvcHVsYXRpb24gKDIwMjApYCkNCmRmX3BvcFtucm93KGRmX3BvcCkgKyAxLF0gPSBjKCJXb3JsZCIsIHdvcmxkX3BvcCkNCg0KIyBBZGQgT3RoZXIgQ291bnRyaWVzIFBvcHVsYXRpb24NCnRvcF9wb3AgPC0gZmlsdGVyKGRmX3BvcCwgZGZfcG9wJGNvdW50cnkgJWluJSB0b3AuY291bnRyaWVzICYgZGZfcG9wJGNvdW50cnkgIT0gIldvcmxkIikNCnRvcF9wb3AgPC0gc3VtKHRvcF9wb3AkYFBvcHVsYXRpb24gKDIwMjApYCAlPiUgYXMubnVtZXJpYygpKQ0Kb3RoZXJzX3BvcCA8LSAod29ybGRfcG9wIC0gdG9wX3BvcCkgDQpkZl9wb3BbbnJvdyhkZl9wb3ApICsgMSxdID0gYygiT3RoZXJzIiwgb3RoZXJzX3BvcCkNCiNWaWV3KGRmX3BvcCkNCg0KZGF0YS5sYXRlc3QgPC0gbWVyZ2UoeCA9IGRhdGEubGF0ZXN0LCB5ID0gZGZfcG9wLCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSANCmRhdGEubGF0ZXN0DQpkYXRhLmxhdGVzdCA8LSByZW5hbWUoZGF0YS5sYXRlc3QsInBvcHVsYXRpb24iID0gIlBvcHVsYXRpb24gKDIwMjApIikNCmRhdGEubGF0ZXN0JHBvcHVsYXRpb24gPC0gZGF0YS5sYXRlc3QkcG9wdWxhdGlvbiAlPiUgYXMubnVtZXJpYygpDQpkYXRhLmxhdGVzdCAgPC0gZGF0YS5sYXRlc3QgJT4lDQogIHNlbGVjdChjKGNvdW50cnksIGNvbmZpcm1lZCwgZGVhdGhzLCBkZWF0aC5yYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuY29uZmlybWVkLCBuZXcuZGVhdGhzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW50LmNvbmZpcm1lZCwgcmVjb3ZlcmVkLCByZWNvdmVyLnJhdGUsIHBvcHVsYXRpb24pKSAlPiUNCiAgbXV0YXRlKGNvbmZpcm0ucmF0ZSA9ICgxMDAgKiBjb25maXJtZWQgLyBwb3B1bGF0aW9uKSAlPiUgcm91bmQoMSkpDQpkYXRhLmxhdGVzdA0KYGBgDQpgYGB7cn0NCmRhdGEubGF0ZXN0ICU+JSBtdXRhdGUoZGVhdGgucmF0ZT1kZWF0aC5yYXRlICU+JSBmb3JtYXQobnNtYWxsPTEpICU+JSBwYXN0ZTAoJyUnKSkNCg0KDQpgYGANCg0KYGBge3J9DQojIyBjb252ZXJ0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgZm9yIGRyYXdpbmcgYXJlYSBwbG90cw0KZGF0YS5sYXRlc3QubG9uZyA8LSBkYXRhLmxhdGVzdCAlPiUgZmlsdGVyKGNvdW50cnkhPSdXb3JsZCcpICU+JQ0KICBnYXRoZXIoa2V5PXR5cGUsIHZhbHVlPWNvdW50LCAtY291bnRyeSkNCiMjIHNldCBmYWN0b3IgbGV2ZWxzIHRvIHNob3cgdGhlbSB3aXRoIHByb3BlciB0ZXh0IGFuZCBpbiBhIGRlc2lyYWJsZSBvcmRlcg0KZGF0YS5sYXRlc3QubG9uZyAlPD4lIG11dGF0ZSh0eXBlPXJlY29kZV9mYWN0b3IodHlwZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm1lZD0nVG90YWwgQ29uZmlybWVkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRocz0nVG90YWwgRGVhdGhzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlYXRoLnJhdGU9J0RlYXRoIFJhdGUgKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5jb25maXJtZWQ9J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldy5kZWF0aHM9J05ldyBEZWF0aHMgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnQuY29uZmlybWVkPSdDdXJyZW50IENvbmZpcm1lZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdmVyLnJhdGUgPSAnUmVjb3ZlciBSYXRlKCUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbmZpcm0ucmF0ZSA9ICdDb25maXJtZWQgUmF0ZSglKScpKQ0KI1ZpZXcoZGF0YS5sYXRlc3QubG9uZykNCmRhdGEub25lLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nVG90YWwgQ29uZmlybWVkJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdUb3RhbCBEZWF0aHMnDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J0N1cnJlbnQgQ29uZmlybWVkJykNCmRhdGEudHdvLmRlbSA8LSBmaWx0ZXIoZGF0YS5sYXRlc3QubG9uZyx0eXBlPT0nRGVhdGggUmF0ZSAoJSknDQogICAgICAgICAgICAgICAgICAgICAgIHwgdHlwZT09J05ldyBDb25maXJtZWQgKGNvbXBhcmVkIHdpdGggb25lIGRheSBiZWZvcmUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdOZXcgRGVhdGhzIChjb21wYXJlZCB3aXRoIG9uZSBkYXkgYmVmb3JlKScNCiAgICAgICAgICAgICAgICAgICAgICAgfCB0eXBlPT0nUmVjb3ZlciBSYXRlKCUpJw0KICAgICAgICAgICAgICAgICAgICAgICB8IHR5cGU9PSdDb25maXJtZWQgUmF0ZSglKScpDQpkYXRhLnR3by5kZW0NCmBgYA0KDQpgYGB7cn0NCiMjIGJhciBjaGFydA0KZGF0YS5vbmUuZGVtICU+JSBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Mywgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMyksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCg0KYGBgDQpgYGB7cn0NCg0KZGF0YS50d28uZGVtICU+JSBnZ3Bsb3QoYWVzKHg9Y291bnRyeSwgeT1jb3VudCwgZmlsbD1jb3VudHJ5LCBncm91cD1jb3VudHJ5KSkgKw0KICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCwgeT1jb3VudCksIHNpemU9Mywgdmp1c3Q9MCkgKw0KICB4bGFiKCcnKSArIHlsYWIoJycpICsNCiAgbGFicyh0aXRsZT1wYXN0ZTAoJ1RvcCAyMCBDb3VudHJpZXMgd2l0aCBNb3N0IENvbmZpcm1lZCBDYXNlcyAtICcsIG1heC5kYXRlLnR4dCkpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSdDb3VudHJ5JywgbGFiZWxzPWFlcyhjb3VudCkpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSdub25lJywNCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMyksDQogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LCBoanVzdD0xKSkgKw0KICBmYWNldF93cmFwKH50eXBlLCBuY29sPTEsIHNjYWxlcz0nZnJlZV95JykNCmBgYA0KDQpgYGB7cn0NCiMjR0RQDQpkZl9nZHAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gZ2RwIikpDQpkZl9nZHAgPC0gYXMuZGF0YS5mcmFtZShkZl9nZHApDQpkZl9nZHAgPC0gcmVuYW1lKGRmX2dkcCwiY291bnRyeSI9IlJlYWwgR0RQIGdyb3d0aCAoQW5udWFsIHBlcmNlbnQgY2hhbmdlKSIpDQpkZl9nZHAgPC0gc2VsZWN0KGRmX2dkcCxjKCJjb3VudHJ5IiwiMjAxMiIsIjIwMTMiLCIyMDE0IiwiMjAxNSIsIjIwMTYiLCIyMDE3IiwiMjAxOCIsIjIwMTkiLCIyMDIwIiwiMjAyMSIpKQ0KZGZfZ2RwDQpkZl9nZHAyMDE5IDwtIHRibChteV9kYiwgc3FsKCJzZWxlY3QgKiBmcm9tIGdkcDE5IikpDQpkZl9nZHAyMDE5IDwtIGFzLmRhdGEuZnJhbWUoZGZfZ2RwMjAxOSkNCmRmX2dkcDIwMTkNCg0KYGBgDQpgYGB7cn0NCiNoZWFsdGhyYW5raW5nDQpkZl9oZWFsdCA8LSB0YmwobXlfZGIsIHNxbCgic2VsZWN0ICogZnJvbSBoZWFsdGhyYW5raW5nIikpDQpkZl9oZWFsdCA8LSBhcy5kYXRhLmZyYW1lKGRmX2hlYWx0KQ0KZGZfaGVhbHQgPC0gc2VsZWN0KGRmX2hlYWx0LGMoImNvdW50cnkiLCJoZWFsdGhDYXJlSW5kZXgiKSkNClZpZXcoZGZfaGVhbHQpDQpgYGANCmBgYHtyfQ0KI3RlbXANCmRmX3RlbXAgPC0gdGJsKG15X2RiLCBzcWwoInNlbGVjdCAqIGZyb20gdGVtcCIpKQ0KZGZfdGVtcCA8LSBhcy5kYXRhLmZyYW1lKGRmX3RlbXApDQpkZl9jaXR5IDwtIHNlbGVjdChkZl90ZW1wLGMoIkNvdW50cnkiLCJDaXR5IikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KSAlPiUgDQogIHJlbmFtZShjaXR5PUNpdHkpDQpudW1vZmNpdHkgPC0gYWdncmVnYXRlKGNpdHkgfiBjb3VudHJ5LCBkYXRhID0gZGZfY2l0eSwgbGVuZ3RoKQ0KZGZfdGVtcCA8LSBzZWxlY3QoZGZfdGVtcCxjKCJDb3VudHJ5IiwiQXByIiwiTWF5IiwiSnVuIiwiSnVsIiwiQXVnIikpICU+JQ0KICByZW5hbWUoY291bnRyeT1Db3VudHJ5KQ0KZGZfdGVtcCA8LSBkYXRhLmZyYW1lKGNvdW50cnk9ZGZfdGVtcFssMV0sYXZnPXJvd01lYW5zKGRmX3RlbXBbLC0xXSkpDQpkZl90ZW1wIDwtIGRmX3RlbXAgJTw+JSBncm91cF9ieShjb3VudHJ5KSAlPiUgc3VtbWFyaXNlKGF2Z19hbGwgPSBtZWFuKGF2ZyxuYS5ybSA9IFRSVUUpKQ0KZGZfdGVtcA0KYGBgDQoNCg0KDQpgYGB7cn0NCiNUb3AgMjAgd2l0aCBnZHANCmRhdGEubG9uZ0dEUCA8LSBkZl9nZHAgJT4lIGdhdGhlcihrZXk9eWVhciwgdmFsdWU9R0RQLCAtYyhjb3VudHJ5KSkNCmRhdGEudG9wIDwtIGRhdGEubGF0ZXN0ICU+JSBmaWx0ZXIoY291bnRyeSE9J1dvcmxkJyYgY291bnRyeSE9J090aGVycycpDQojZGF0YS50b3AgPC0gaGVhZChkYXRhLnRvcCwyMCkNClZpZXcoZGF0YS50b3ApDQpkYXRhLmdkcCA8LSBmaWx0ZXIoZGF0YS5sb25nR0RQLHllYXI9PScyMDIwJykNCiNWaWV3KGRhdGEuZ2RwKQ0KI21lcmdlDQptZXJnY291bnRyeSA9IGZ1bmN0aW9uKGRhdGExLGRhdGEyKXsNCiAgZGF0YSA8LSBtZXJnZSh4ID0gZGF0YTEsIHkgPSBkYXRhMiwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgDQogIHJldHVybihkYXRhKQ0KfQ0KZGF0YS50b3Aud29ybGQgPC0gbWVyZ2UoeCA9IGRhdGEudG9wLCB5ID0gZGZfZ2RwMjAxOSwgYnkgPSAiY291bnRyeSIsIGFsbC54ID0gVFJVRSkgJT4lIA0KICBzZWxlY3QoLWMoY29kZSxyYW5rLG5ldy5jb25maXJtZWQsbmV3LmRlYXRocyxjdXJyZW50LmNvbmZpcm1lZCxwb3B1bGF0aW9uKSkgJT4lIA0KICByZW5hbWUoR0RQPSJHRFAgKG1pbGxpb25zIG9mIFVTIGRvbGxhcnMpIikNCmRhdGEudG9wLndvcmxkIDwtIG1lcmdlKHggPSBkYXRhLnRvcC53b3JsZCwgeSA9IGRmX2hlYWx0LCBieSA9ICJjb3VudHJ5IiwgYWxsLnggPSBUUlVFKSAlPiUNCiAgcmVuYW1lKGhlYWx0aGNhcmU9ImhlYWx0aENhcmVJbmRleCIpDQojZGF0YS50b3Aud29ybGQgPC0gbWVyZ2NvdW50cnkoZGF0YS50b3Aud29ybGQsIGRmX3RlbXApDQpWaWV3KGRhdGEudG9wLndvcmxkKQ0Kbm9ybWFsaXplID0gZnVuY3Rpb24oZGF0YSl7DQogICNyZXR1cm4gKChkYXRhIC0gbWluKGRhdGEsbmEucm0gPSBUUlVFKSkvKG1heChkYXRhLG5hLnJtID0gVFJVRSkgLSBtaW4oZGF0YSxuYS5ybSA9IFRSVUUpKSkNCiAgeiA8LSBzY2FsZShkYXRhKTsNCiAgdGFuaCh6LzIpDQp9DQpub3JtX2RhdGEgPSBhcy5kYXRhLmZyYW1lKGFwcGx5KGRhdGEudG9wLndvcmxkWywyOjldLDIsbm9ybWFsaXplKSkNCmNvcnJfZGF0YSA8LSBub3JtX2RhdGENCm5vcm1fZGF0YSRjb3VudHJ5IDwtIGMoIkFyZ2VudGluYSIsIkJhbmdsYWRlc2giLCJCcmF6aWwiLCJDaGlsZSIsIkNvbG9tYmlhIiwiRnJhbmNlIiwiR2VybWFueSIsIkluZGlhIiwiSXJhbiIsIkl0YWx5IiwiTWV4aWNvIiwiUGFraXN0YW4iLCJQZXJ1IiwiUnVzc2lhIiwic2F1ZGkgQXJhYmlhIiwiU291dGggQWZyaWNhIiwiU3BhaW4iLCJUdXJrZXkiLCJVbml0ZWQgS2luZ2RvbSIsIlVTIikNClZpZXcobm9ybV9kYXRhKQ0Kbm9ybV9kYXRhX3Bsb3QgPC0gc2VsZWN0KG5vcm1fZGF0YSwiY291bnRyeSIsImNvbmZpcm0ucmF0ZSIsImRlYXRoLnJhdGUiLCJyZWNvdmVyLnJhdGUiLCJoZWFsdGhjYXJlIiwiR0RQIikNCm5vcm1fZGF0YV9wbG90ICU8PiUgZ2F0aGVyKGtleT10eXBlLCB2YWx1ZT1jb3VudCwgLWMoY291bnRyeSkpDQpsZXZlbF9vcmRlciA8LSBmYWN0b3Iobm9ybV9kYXRhX3Bsb3QkdHlwZSwgDQogICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSBjKCJHRFAiLCJoZWFsdGhjYXJlIiwicmVjb3Zlci5yYXRlIiwiZGVhdGgucmF0ZSIsImNvbmZpcm0ucmF0ZSIpKQ0KZ2dwbG90KGRhdGEgPSBub3JtX2RhdGFfcGxvdCwgYWVzKHg9Y291bnRyeSwgeT1sZXZlbF9vcmRlciwgZmlsbD1jb3VudCkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAicGluayIsIGhpZ2ggPSAiYmx1ZSIpICsNCiAgeGxhYigiIikgKw0KICB5bGFiKCIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLHZqdXN0ID0gMSkpKw0KICB0aGVtZSgNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgI2xlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApDQogIA0KYGBgDQoNCg0KYGBge3J9DQojcmFuayBHRFANCmRhdGEudG9wLmhpZ2h0IDwtIGRhdGEuZ2RwICU+JSBzZWxlY3QoY291bnRyeSwgeWVhcixHRFApICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoZGVzYyhHRFApKSkNCmRhdGEudG9wLmhpZ2h0DQprIDwtIDE1DQp0b3AuZ2RwIDwtIGRhdGEudG9wLmhpZ2h0ICU+JSANCiAgI2ZpbHRlcihyYW5raW5nIDw9IGsgKyAxKSAlPiUgDQogIGFycmFuZ2UocmFua2luZykNCnRvcC5nZHAgPC0gaGVhZCh0b3AuZ2RwLDIxKQ0KZGF0YS50b3AubG93IDwtIGRhdGEuZ2RwICU+JSBzZWxlY3QoY291bnRyeSwgeWVhcixHRFApICU+JQ0KICBtdXRhdGUocmFua2luZyA9IGRlbnNlX3JhbmsoR0RQKSkNCmxvdy5nZHAubG9uZyA8LSBkYXRhLnRvcC5sb3cgJT4lIA0KICAjZmlsdGVyKHJhbmtpbmcgPD0gayArIDEpICU+JSANCiAgYXJyYW5nZShyYW5raW5nKQ0KVmlldyhsb3cuZ2RwLmxvbmcpDQpsb3cuZ2RwIDwtIGhlYWQobG93LmdkcC5sb25nLDIzKQ0KbG93LmdkcA0KYGBgDQoNCg0KDQpgYGB7cn0NCiNjb3JyZWxhdGlvbg0KY29ycl9kYXRhICU8PiUgc2VsZWN0KGMoR0RQLGNvbmZpcm0ucmF0ZSxkZWF0aC5yYXRlLHJlY292ZXIucmF0ZSxoZWFsdGhjYXJlKSkNCmhlYWQoY29ycl9kYXRhKQ0KY29yKGNvcnJfZGF0YSkNCmdnY29ycnBsb3QoY29yKGNvcnJfZGF0YSksaGMub3JkZXIgPSBUUlVFLA0KICAgICAgICAgICBvdXRsaW5lLmNvbG9yID0gIndoaXRlIiwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzZEOUVDMSIsIndoaXRlIiwiI0U0NjcyNiIpLA0KICAgICAgICAgICBsYWIgPSBUUlVFKQ0KYGBgDQoNCg0KDQo=